package com.demo.chapter11future.asyncadddiscount;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.demo.chapter11future.asyncadddiscount.ExchangeService.Money;
import com.demo.chapter11future.sync.Shop;

public class Demo {
	
	private static List<Shop> shops = Arrays.asList(new Shop("BestPrice"), new Shop("LetsSaveBig"), new Shop("MyFav"), new Shop("BuyAll"));
	private final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100));
	
	//从shops列表里获取product产品的价格和折扣
	public List<String> findPrice(String product){
		return shops.stream().map(shop -> shop.getPrice(product))
				.map(Quote::parse)
				.map(Discount::applyDiscount)
				.collect(Collectors.toList());
	}
	
	public List<String> findPrice2(String product){
		Stream<CompletableFuture<String>> priceFuturesStream = shops
				.stream()
				.map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
				//由于一般情况下，解析操作不涉及任何远程服务，也不会进行io操作，所以可以采用同步操作，不会带来太多延迟，所以可以使用thenApply
				.map(future -> future.thenApply(Quote::parse))
				//在另一个异步任务构造期望的Future，申请折扣
				//thenCompose允许你对两个异步操作进行流水线，第一个操作完成时，将其结果作为参数传递给第二个操作
				.map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)));
		List<CompletableFuture<String>> priceFutures = priceFuturesStream.collect(Collectors.toList());
		
		return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
	}
	
	public void findPrice3(String product){
		Stream<CompletableFuture<Double>> priceFuturesStream = shops.stream()
			.map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
			.map(future -> future.thenApply(Quote::parse))
			//thenCombine可以把两个不相干的CompletableFuture的结果整合起来，而且你也不希望第一个任务完成后再执行第二个任务
			//thenCombine接受的第二个参数：当两个CompletableFuture对象完成计算后，合并它们的
			.map(future -> future.thenCombine(CompletableFuture.supplyAsync(() -> ExchangeService.getRate(Money.EUR, Money.USD)), (quote, rate) -> quote.getPrice() * rate));
		priceFuturesStream.map(CompletableFuture::join).forEach(System.out::println);
	}

	public static void main(String[] args) {
		Demo test = new Demo();
		
		//用同步的方式获取价格，因为只提供了同步api
		long start = System.nanoTime();
		test.findPrice("iphone");
		long incocationTime = ((System.nanoTime() - start) / 1_000_000);
        System.out.println("Invocation returned after " + incocationTime + " msecs");
        
        long start2 = System.nanoTime();
        test.findPrice2("iphone");
        long incocationTime2 = ((System.nanoTime() - start2) / 1_000_000);
        System.out.println("Invocation returned after " + incocationTime2 + " msecs");
        
        test.findPrice3("iphone6s");
        
	}

}
